<script setup>

import {ref} from "vue";
import {ElMessageBox} from "element-plus";
import {myDelete, myDeleteBatch} from "../request";
import {isNotNull, datetimeFormat} from "../util";
import router from "../router";

let parent = defineProps({

  // 模块名称，必填，用于删除和批量删除业务
  module: {type: String, required: true},

  // 表格行数据，必填
  records: {type: Array, required: true},

  /*
   * 表格列数据，必填：
   *
   * label: 列名数组，全类型通用
   * prop: 属性名，全类型通用
   * width: 列宽，全类型通用
   * sortable: 是否支持排序，默认 true，全类型通用
   * tooltip: 是否支持溢出内容的鼠标经过提示，默认 false，全类型通用
   * type: 表格列类型，支持 tag, star, card, img, icon, text 6种，默认 text：
   *     type = tag: 标签
   *         prefix: 前缀文字
   *         suffix: 后缀文字
   *         format: 前置格式化函数
   *         tagType: 标签颜色，默认 "warning"
   *         tagTypeFn: 标签颜色函数
   *     type = card: 卡片
   *         prefix: 前缀文字
   *         suffix: 后缀文字
   *         format: 前置格式化函数
   *     type = img: 图片
   *         minio: MINIO地址前缀函数
   *         imgWidth: 图片宽度，默认 100px
   *         imgHeight: 图片高度，默认 100px
   *     type = icon: 图标
   *         iconType: 图标颜色，默认 "primary"
   *         iconSize: 图标尺寸，默认 "small"
   *     type = text: 文本
   *         prefix: 前缀文字
   *         suffix: 后缀文字
   *         format: 前置格式化函数
   */
  columns: {type: Array, required: true},

  /* 分页信息
   *
   * pageNum: 当前第几页，默认 1
   * pageSize: 每页多少条，默认 5
   * total: 一共多少条
   * callback: 分页回调函数
   */
  pageInfo: {type: Object, required: true},

  /* 表格尾按钮：
   *
   * label: 按钮文案，默认 "普通按钮"
   * type: 按钮颜色，默认 "success"
   * icon: 按钮图标，默认 "InfoFilled"
   * callback: 当点击按钮时触发的回调函数
   */
  buttons: {type: Array, required: false},
  // 当点击添加按钮时跳入的路由组件名称
  insertPage: {type: String, required: false},
  // 跳入添加页面之前，附加的参数，JSON格式，默认存储到 sessionStorage 中
  insertPageParam: {type: Object, required: false},
  // 当点击修改按钮时跳入的路由组件名称
  updatePage: {type: String, required: false},
  // 跳入修改页面之前，附加的参数，JSON格式，默认存储到 sessionStorage 中
  updatePageParam: {type: Object, required: false},
  // 单条删除API方法
  deleteApi: {type: Function, required: false},
  // 打印报表API方法
  excelApi: {type: Function, required: false},
  // 批量删除API方法
  deleteBatchApi: {type: Function, required: false},
  // 单条删除和批量删除结束后的回调方法
  deleteCallback: {type: Function, required: false},
  // 每页多少条可选数量，默认 [1, 5, 10, 18]
  pageSizes: {type: Array, required: false, default: [1, 5, 10, 18]},
});

/* ==================== 处理表格多级标题 ==================== */

// 表格列数据对象
let columnsObj = {};

/*
 * 原格式:
 *
 * [
 *   {label: ['a', 'b'], prop: 'id01'},
 *   {label: ['a', 'c'], prop: 'id02'},
 *   {label: ['b', 'c'], prop: 'id03'},
 * ]
 *
 * 新格式:
 *
 * {
 *   'a': [{label: 'b', prop: 'id01'}, {label: 'c', prop: 'id02'}],
 *   'b': [{label: 'c', prop: 'id03'}],
 * }
 *
 */

for (let column of parent['columns']) {
  // '主键' 或 ['主键'] -> ['基本信息', '主键']
  if (typeof column['label'] === 'string' || column['label'].length === 1) {
    column['label'] = ['基本信息', column['label']]
  }
  // 获取一级标题
  let parentName = column['label'][0];
  column['label'] = column['label'][1];
  // 若 columnsObj 中已经存在该一级标题对应的数组，则取出该数组，并加入该一级标题
  if (columnsObj.hasOwnProperty(parentName)) {
    columnsObj[parentName].push(column);
  }
  // 若 columnsObj 中不经存在该一级标题对应的数组，则创建新数组，再加入该一级标题
  else {
    columnsObj[parentName] = [column];
  }
}


/* ==================== 处理表格数据显示 ==================== */

/** 处理表格数据显示: row['user.username'] -> row['user']['username'] */
function toMustache(row, prop) {
  let propArray = prop.split('.');
  for (let i = 0, j = propArray.length; i < j; i++) {
    // i=0: row = row['user']
    // i=1: row = row['user']['username']
    if (row) row = row[propArray[i]];
  }
  return row;
}


/* ==================== 查看card内容详情 ==================== */

/**
 * 查看card内容详情
 *
 * @param row 当前行数据
 * @param prop 属性
 */
function cardDetail(row, prop) {
  ElMessageBox.alert(row[prop], '记录详情', {
    confirmButtonText: '了解',
    callback: () => {
    },
  });
}


/* ==================== 查看详情 ==================== */

// 菜单详情抽屉 + 菜单详情列表数据
let detailDrawer = ref(), detailItems = [];

/** 查看记录详情 */
function detail(record) {
  // 每次清空，防止追加
  detailItems = [];
  // 填充表格数据到详情列表中
  for (let i in parent['columns']) {
    let item = parent['columns'][i];
    item['value'] = toMustache(record, item['prop']);
    detailItems.push(item);
  }
  // 填充 创建时间 和 修改时间 信息
  detailItems.push({label: '创建时间', value: record['created'], format: datetimeFormat});
  detailItems.push({label: '修改时间', value: record['updated'], format: datetimeFormat});
  // 打开抽屉
  detailDrawer.value = true;
}


/* ==================== 单条删除 ==================== */

/**
 * 调用 myDelete() 工具删除该行数据
 *
 * @param row 当前行数据
 */
function remove(row) {
  myDelete({
    id: row['id'],
    api: parent['deleteApi'],
    args: {module: parent['module']},
    callback: parent['deleteCallback']
  });
}


/* ==================== 批量删除 ==================== */

// 批量删除主键数组
let ids = [];
// 当表格首列的多选框被选中或取消选中时触发
function selectionChange(rows) {
  // 记录当前被选中的行
  ids = rows.map(e => e['id']);
}

/** 调用 myDeleteBatch() 工具批量删除数据*/
function removeBatch() {
  myDeleteBatch({
    ids: ids,
    api: parent['deleteBatchApi'],
    args: {module: parent['module']},
    callback: parent['deleteCallback']
  });
}


/* ==================== 单条添加 ==================== */

/** 路由到指定的添加页面 */
function insert() {
  if (isNotNull(parent['insertPage'])) {
    if (isNotNull(parent['insertPageParam'])) {
      sessionStorage.setItem('insertPageParam', JSON.stringify(parent['insertPageParam']));
    }
    router.push(parent['insertPage']);
  } else {
    ElMessageBox.alert('功能还未开发，敬请期待！', '提示', {type: 'warning'});
  }
}


/* ==================== 单条修改 ==================== */

/**
 * 存储当前行数据，并路由到指定的修改页面。
 *
 * @param row 当前行数据
 */
function update(row) {
  if (isNotNull(parent['updatePage'])) {
    if (isNotNull(parent['updatePageParam'])) {
      sessionStorage.setItem('updatePageParam', JSON.stringify(parent['updatePageParam']));
    }
    sessionStorage.setItem('row', JSON.stringify(row));
    router.push(parent['updatePage']);
  } else {
    ElMessageBox.alert('功能还未开发，敬请期待！', '提示', {type: 'warning'});
  }
}

/* ==================== 打印报表 ==================== */

/** 调用对应API，打印报表 */
function excel() {
  if (isNotNull(parent['excelApi'])) {
    parent['excelApi']();
  } else {
    ElMessageBox.alert('功能还未开发，敬请期待！', '提示', {type: 'warning'});
  }
}

/* ==================== 处理表格属性 ==================== */

/** 是否使用 tooltip 属性 */
function useTooltip(item) {
  // 配置优先
  if (isNotNull(item['tooltip'])) return item['tooltip'];
  // 评分，图片，图标和卡片都不使用 tooltip 属性，其余情况使用 tooltip 属性
  return item['type'] !== 'star' &&
      item['type'] !== 'img' &&
      item['type'] !== 'icon' &&
      item['type'] !== 'card';
}

/** 是否使用 sortable 属性 */
function useSortable(item) {
  // 配置优先
  if (isNotNull(item['sortable'])) return item['sortable'];
  // 评分，图片，图标和卡片都不使用 sortable 属性，其余情况使用 sortable 属性
  return item['type'] !== 'star' &&
      item['type'] !== 'img' &&
      item['type'] !== 'icon' &&
      item['card'] !== 'icon';
}

/** 是否使用 width 属性 */
function useWidth(item) {
  // 配置优先
  if (isNotNull(item['width'])) return item['width'];
  // 卡片默认宽度 300px
  if (item['type'] === 'card') return 300;
  // 标签默认宽度 110px
  if (item['type'] === 'tag') return 110;
  // 图片默认宽度 80px
  if (item['type'] === 'img') return 80;
  // 其他默认宽度 200px
  return 200;
}

</script>



<template>

  <el-table class="my-table"
            tooltip-effect="light"
            size="small"
            stripe
            highlight-current-row
            :data="records"
            @selection-change="selectionChange">

    <el-table-column class="head-col"
                     label="序号"
                     align="center"
                     fixed="left">
      <el-table-column type="selection" width="40"/>
      <el-table-column type="index" width="40"/>
    </el-table-column>

    <el-table-column class="body-col"
                     align="center"
                     v-for="(childrenArray, parentLabel) in columnsObj"
                     :key="childrenArray"
                     :label="parentLabel">
      <el-table-column class="body-col-inner"
                       v-for="item in childrenArray"
                       :key="item"
                       :label="item['label']"
                       :property="item['prop']"
                       :sortable="useSortable(item)"
                       :show-overflow-tooltip="useTooltip(item)"
                       :width="useWidth(item)">
        <template #default="scope">
          <el-tag v-if="item['type'] === 'tag' && isNotNull(toMustache(scope.row, item['prop']))"
                  :type="isNotNull(item['tagTypeFn']) ? item['tagTypeFn'](scope.row[item['prop']]) :
                         isNotNull(item['tagType']) ? item['tagType'] : 'warning'">
            <span>{{ item['prefix'] }}</span>
            <span v-if="isNotNull(item['format'])">{{ item['format'](toMustache(scope.row, item['prop'])) }}</span>
            <span v-else>{{ toMustache(scope.row, item['prop']) }}</span>
            <span>{{ item['suffix'] }}</span>
          </el-tag>

          <el-rate v-else-if="item['type'] === 'star' && isNotNull(scope.row[item['prop']])"
                   disabled show-score
                   v-model="scope.row[item['prop']]"
                   text-color="#ff9900"
                   score-template="{value}"/>
          <el-card v-else-if="item['type'] === 'card' && isNotNull(toMustache(scope.row, item['prop']))"
                   @click="cardDetail(scope.row, item['prop'])">
            <span>{{ item['prefix'] }}</span>
            <span v-if="isNotNull(item['format'])">{{ item['format'](toMustache(scope.row, item['prop'])) }}</span>
            <span v-else>{{ toMustache(scope.row, item['prop']) }}</span>
            <span>{{ item['suffix'] }}</span>
          </el-card>

          <el-image v-else-if="item['type'] === 'img' && isNotNull(toMustache(scope.row, item['prop']))"
                    fit="fill"
                    preview-teleported
                    :src="item['minio'](toMustache(scope.row, item['prop']))"
                    :preview-src-list="[item['minio'](toMustache(scope.row, item['prop']))]"
                    :style="{width: item['imgWidth'], height: item['imgHeight']}"/>
          <el-button v-else-if="item['type'] === 'icon' && isNotNull(toMustache(scope.row, item['prop']))"
                     plain
                     :type="isNotNull(item['iconType']) ? item['iconType'] : 'primary'"
                     :size="isNotNull(item['iconSize']) ? item['iconSize'] : 'small'"
                     :icon="toMustache(scope.row, item['prop'])"/>
          <div class="text" v-else>
            <span>{{ item['prefix'] }}</span>
            <span v-if="isNotNull(item['format'])">{{ item['format'](toMustache(scope.row, item['prop'])) }}</span>
            <span v-else>{{ toMustache(scope.row, item['prop']) }}</span>
            <span>{{ item['suffix'] }}</span>
          </div>
        </template>
      </el-table-column>
    </el-table-column>

    <el-table-column class="time-col"
                     label="时间信息"
                     align="center">
      <el-table-column class="created-col"
                       label="记录首次创建时间"
                       property="created"
                       sortable
                       :width="150"
                       :formatter="row => datetimeFormat(row['created'])"/>
      <el-table-column class="updated-col"
                       label="记录最后修改时间"
                       property="updated"
                       sortable
                       :width="150"
                       :formatter="row => datetimeFormat(row['updated'])"/>
    </el-table-column>

    <el-table-column class="foot-col"
                     align="center"
                     fixed="right"
                     :width="170">
      <template #header>
        <el-button class="insert-btn"
                   plain
                   size="small"
                   icon="CirclePlusFilled"
                   type="primary"
                   title="单增一条记录"
                   @click="insert"/>
        <el-button class="delete-batch-btn"
                   plain
                   size="small"
                   icon="DeleteFilled"
                   type="danger"
                   title="批量删除记录"
                   @click="removeBatch"/>
        <el-button class="excel-btn"
                   plain
                   size="small"
                   icon="Grid"
                   type="warning"
                   title="打印数据报表"
                   @click="excel"/>
      </template>

      <template #default="scope">
        <el-row :gutter="0">
          <el-col class="info-btn-col" :span="12">
            <el-button class="info-btn"
                       link
                       size="small"
                       type="info"
                       icon="InfoFilled"
                       @click="detail(scope.row)">
              查看详情
            </el-button>
          </el-col>
          <el-col class="delete-btn-col" :span="12">
            <el-button class="delete-btn"
                       link
                       size="small"
                       type="danger"
                       icon="Delete"
                       @click="remove(scope.row)">
              删除记录
            </el-button>
          </el-col>
          <el-col class="update-btn-col" :span="12">
            <el-button class="update-btn"
                       link
                       size="small"
                       type="warning"
                       icon="Edit"
                       @click="update(scope.row)">
              修改记录
            </el-button>
          </el-col>

          <el-col class="custom-btn-col"
                  :span="12"
                  v-for="button in buttons"
                  :key="button">
            <el-button class="custom-btn"
                       link
                       size="small"
                       :type="isNotNull(button['type']) ? button['type'] : 'success'"
                       :icon="isNotNull(button['icon']) ? button['icon'] : 'InfoFilled'"
                       @click="button['callback'](scope.row)">
              {{ isNotNull(button['label']) ? button['label'] : '普通按钮' }}
            </el-button>
          </el-col>
        </el-row>
      </template>
    </el-table-column>
    <!--用于调控表格宽度，不设置宽度，则宽度自撑开到100%-->
    <el-table-column/>
  </el-table>

  <el-drawer class="detail-drawer"
             v-model="detailDrawer"
             title="记录详情"
             size="30%">
    <el-descriptions class="my-list"
                     border
                     label-align="center"
                     :column="1">
      <el-descriptions-item class="list-item"
                            v-for="item in detailItems"
                            :key="item"
                            :label="item['label']"
                            :span="isNotNull(item['colspan']) ? item['colspan'] : 1">
        <template #default>
          <el-tag v-if="item['type'] === 'tag' && isNotNull(item['value'])"
                  :type="isNotNull(item['tagTypeFn']) ? item['tagTypeFn'](item['value']) :
                       isNotNull(item['tagType']) ? item['tagType'] : 'warning'">
            <span>{{ item['prefix'] }}</span>
            <span v-if="isNotNull(item['format'])">{{ item['format'](item['value']) }}</span>
            <span v-else>{{ item['value'] }}</span>
            <span>{{ item['suffix'] }}</span>
          </el-tag>
          <el-rate v-else-if="item['type'] === 'star' && isNotNull(item['value'])"
                   disabled
                   show-score
                   v-model="item['value']"
                   text-color="#ff9900"
                   score-template="{value}"/>
          <el-card v-else-if="item['type'] === 'card' && isNotNull(item['value'])"
                   :style="{height: item['height'] + 'px'}">
            <span>{{ item['prefix'] }}</span>
            <span v-if="isNotNull(item['format'])">{{ item['format'](item['value']) }}</span>
            <span v-else>{{ item['value'] }}</span>
            <span>{{ item['suffix'] }}</span>
          </el-card>
          <el-image v-else-if="item['type'] === 'img' && isNotNull(item['value'])"
                    fit="fill"
                    preview-teleported
                    :src="item['minio'](item['value'])"
                    :preview-src-list="[item['minio'](item['value'])]"
                    :style="{width: item['imgWidth'] + 'px', height: item['imgHeight'] + 'px'}"/>

          <el-button v-else-if="item['type'] === 'icon' && isNotNull(item['value'])"
                     plain
                     :type="isNotNull(item['iconType']) ? item['iconType'] : 'primary'"
                     :size="isNotNull(item['iconSize']) ? item['iconSize'] : 'small'"
                     :icon="item['value']">
          </el-button>
          <div class="text" v-else>
            <span>{{ item['prefix'] }}</span>
            <span v-if="isNotNull(item['format'])">{{ item['format'](item['value']) }}</span>
            <span v-else>{{ item['value'] }}</span>
            <span>{{ item['suffix'] }}</span>
          </div>
        </template>
      </el-descriptions-item>
    </el-descriptions>
  </el-drawer>

  <el-pagination class="my-pager"
                 layout="total, sizes, prev, pager, next, jumper"
                 :background="true"
                 :page-sizes="pageSizes"
                 :current-page="isNotNull(pageInfo['pageNum']) ? pageInfo['pageNum'] : 1"
                 :page-size="isNotNull(pageInfo['pageSize']) ? pageInfo['pageSize'] : 5"
                 :total="pageInfo['total']"
                 @size-change="pageSize => pageInfo['callback'](pageInfo['pageNum'], pageSize)"
                 @current-change="pageNum => pageInfo['callback'](pageNum, pageInfo['pageSize'])"/>
</template>

<style scoped lang="scss">

.my-table {
  height: 500px; // 高度
  .el-image {
    width: 100%; // 宽度
    height: 50px; // 高度
    margin-bottom: -10px; // 下边距
  }
  .el-card {
    height: 62px; // 高度（大概2行文字）
    --el-card-padding: 10px !important; // 内边距
    white-space: normal !important; // 自动换行
    overflow-y: scroll !important; // 垂直溢出滚动
  }
}
.foot-col-inner {
  text-align: left; // 居左
}
:deep(.el-descriptions__label.el-descriptions__cell.is-bordered-label) {
  min-width: 107px !important; // 最小列宽107
  max-width: 137px !important; // 最大列宽137
}
.my-list {
  .el-image {
    width: 100px; // 宽度
    height: 100px; // 高度
  }
  .el-card {
    max-height: 420px; // 最大高度（不超过15行）
    --el-card-padding: 10px !important; // 内边距
    white-space: normal !important; // 自动换行
    overflow-y: scroll !important; // 垂直溢出滚动
  }
}
.my-pager {
  margin: 20px; // 外边距
  justify-content: center; // 自居中
}
</style>